﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using Cureos.Measures;
using SensorDrone.Net;
using SensorDroneStudio.Common;
using SensorDroneStudio.Models;
using SimpleMvvmToolkit;

namespace SensorDroneStudio.ViewModels
{
    public class SensorViewModel : ViewModelBase<SensorViewModel>
    {
        private readonly IDisposable _subscriber;
        private ObservableCollection<IUnit> _allUnits;
        private bool _alternateUnitsAvailable;
        private string _name;
        private ISensorPlotModel _plotModel;
        private IUnit _selectedUnit;
        private string _units;
        private string _value;

        public SensorViewModel()
        {
            RegisterToReceiveMessages(MessageTokens.Scanning, OnScanning);
            RegisterToReceiveMessages<ISensorDrone>(MessageTokens.DroneSelected, OnDroneSelected);

            Name = "N/A";
            Value = "0.0";
            Units = "C";
            _allUnits = new ObservableCollection<IUnit>();
        }

        public SensorViewModel(IObservable<Timestamped<Reading>> observable, string sensor, ISensorPlotModel plotModel)
            : this()
        {
            Name = sensor;
            _plotModel = plotModel;
            _plotModel.Clear();
            _subscriber = observable.Where(reading => reading.Value.Name == sensor).Subscribe(OnNextSample);
        }

        public bool AlternateUnitsAvailable
        {
            get { return _alternateUnitsAvailable; }
            private set
            {
                _alternateUnitsAvailable = value;
                NotifyPropertyChanged(vm => AlternateUnitsAvailable);
            }
        }

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    NotifyPropertyChanged(vm => vm.Name);
                }
            }
        }

        public string Value
        {
            get { return _value; }
            set
            {
                if (_value != value)
                {
                    _value = value;
                    NotifyPropertyChanged(vm => vm.Value);
                }
            }
        }

        public string Units
        {
            get { return _units; }
            set
            {
                if (_units != value)
                {
                    _units = value;
                    NotifyPropertyChanged(vm => vm.Units);
                }
            }
        }

        public ISensorPlotModel PlotModel
        {
            get { return _plotModel; }
            set
            {
                if (_plotModel != value)
                {
                    _plotModel = value;
                    NotifyPropertyChanged(vm => PlotModel);
                }
            }
        }

        public ObservableCollection<IUnit> AlternateUnits
        {
            get { return _allUnits; }
            set
            {
                _allUnits = value;
                NotifyPropertyChanged(vm => AlternateUnits);
            }
        }

        public IUnit SelectedUnit
        {
            get { return _selectedUnit; }
            set { _selectedUnit = value; }
        }

        private void OnScanning(object sender, NotificationEventArgs args)
        {
            if (_subscriber != null)
                _subscriber.Dispose();
        }

        private void OnDroneSelected(object sender, NotificationEventArgs<ISensorDrone> args)
        {
            if (_subscriber != null)
                _subscriber.Dispose();
        }

        private void OnNextSample(Timestamped<Reading> reading)
        {
            if (reading.Value.Measure != null)
            {
                Dispatcher.BeginInvoke(() =>
                {
                    if (!_allUnits.Any())
                    {
                        foreach (IUnit unit in reading.Value.AlternateUnits)
                        {
                            _allUnits.Add(unit);
                        }
                    }
                    AlternateUnitsAvailable = reading.Value.AlternateUnits.Any();
                });
                if (_selectedUnit != null)
                {
                    Value = reading.Value.Measure.GetAmount(_selectedUnit).ToString("F2");
                    Units = _selectedUnit.ToString();
                }
                else
                {
                    Value = reading.Value.Value;
                    Units = reading.Value.Unit;
                }
            }
            else
            {
                Value = reading.Value.Value;
                Units = reading.Value.Unit;
            }
            PlotModel.Add(reading);
        }
    }
}